package tools

import (
	"awesome-devops/helper"
	"fmt"
	"gitee.com/jjawesomejj/awesome-util/helper/commonhelper"
	"gitee.com/jjawesomejj/awesome-util/helper/timeHelper"
	"regexp"
	"strconv"
	"strings"
)

const SPLIT_CHAR string = "$$$"

/*
*
@author jjawesome
@description 对git命令进行封装
*/
type GitCommand struct {
	WorkDir        string
	ProjectDir     string
	GitSshUrl      string
	GitProjectPath string
	Std            *Standard
}
type GitLog struct {
	CommitId string `json:"commit_id"`
	Author   string `json:"author"`
	Date     string `json:"date"`
	Comment  string `json:"comment"`
}
type GitDiffFileItem struct {
	oldStartLine int
	newStartLine int
	Content      string `json:"content"`
}

type GitDiff struct {
	FileName  string            `json:"file_name"`
	ModifyLog []GitDiffFileItem `json:"modify_log"`
}

func (gitCommand *GitCommand) clone() (output string, err error) {
	baseCommand := "cd " + gitCommand.WorkDir + "/" + gitCommand.ProjectDir + " && "
	command := "git clone " + gitCommand.GitSshUrl
	return GetKernelDriver().ExecShellOrBash(baseCommand+command, false)
}

func (gitCommand *GitCommand) GetProjectAbsDir() string {
	if strings.Index(gitCommand.GitProjectPath, ".") != -1 {
		//相对路径
		return helper.GetCurrentRunningPath() + "/" + gitCommand.GitProjectPath
	} else {
		//绝对路径
		return gitCommand.GitProjectPath
	}
}

func (gitCommand *GitCommand) Diff(oldCommitId string, newCommitId string) (gitDiffOutput []GitDiff, err error) {
	tmpFileName := helper.GetCurrentRunningPath() + "/" + commonhelper.Md5(commonhelper.Rand(20)+timeHelper.Now()) + "_git.log"
	command := "git diff " + oldCommitId + " " + newCommitId + " > " + tmpFileName
	_, err = gitCommand.baseCommand(command)
	output := helper.ReadFileAsString(tmpFileName)
	helper.RemoveAllFile(tmpFileName)
	if err == nil {
		output := "\n" + output
		regex, err := regexp.Compile("\ndiff --git (.*?)\n")
		diff := make([]GitDiff, 0)
		if err == nil {
			fileList := regex.Split(output, len(output))
			fileNameRegex, _ := regexp.Compile("\n--- a(.*?)\n")          //匹配文件名称
			fileNameRegexAdd, _ := regexp.Compile("\n\\+\\+\\+ b(.*?)\n") //匹配文件名称
			modifyIndexRegex, _ := regexp.Compile("@ -(.*?),")
			contentRegex, _ := regexp.Compile("@(.*?)\\+(.*?),(.*?)@@(.*?)\n")
			contentRegexItem, _ := regexp.Compile("@(.*?)@@(.*?)\n")
			for _, fileItem := range fileList {
				fileNameList := fileNameRegex.FindStringSubmatch(fileItem)
				if len(fileNameList) != 2 {
					fileNameList = fileNameRegexAdd.FindStringSubmatch(fileItem)
					if len(fileNameList) != 2 {
						continue
					}
				}
				fileName := fileNameList[1]
				modifiedList := strings.Split(fileItem, "\n@")
				modifyLogs := make([]GitDiffFileItem, 0)
				gitDiff := GitDiff{
					FileName:  fileName,
					ModifyLog: modifyLogs,
				}
				for index, modifiedItem := range modifiedList {
					if index == 0 {
						continue
					}
					indexList := modifyIndexRegex.FindStringSubmatch(modifiedItem)
					contentList := contentRegexItem.Split(modifiedItem, len(modifiedItem))
					newFileIndexMatchs := contentRegex.FindStringSubmatch(modifiedItem)
					if len(contentList) > 1 && len(indexList) > 1 && len(newFileIndexMatchs) == 5 {
						line, err := strconv.Atoi(indexList[1])
						newLine, err := strconv.Atoi(newFileIndexMatchs[2])
						if err == nil {
							gitDiffItem := GitDiffFileItem{
								oldStartLine: line,
								newStartLine: newLine,
								Content:      contentList[1],
							}
							gitDiffItem.handleContent()
							modifyLogs = append(modifyLogs, gitDiffItem)
						}
					}
				}
				gitDiff.ModifyLog = modifyLogs
				diff = append(diff, gitDiff)
			}
			return diff, err
		}
	}
	return nil, err
}
func (gitCommand *GitCommand) ResolvePath() string {
	dirs := strings.Split(gitCommand.GitSshUrl, "/")
	dir := dirs[len(dirs)-1]
	dirNotFix := strings.ReplaceAll(dir, ".git", "")
	gitCommand.GitProjectPath = gitCommand.WorkDir + "/" + gitCommand.ProjectDir + "/" + dirNotFix
	return "/" + dirNotFix
}
func (gitCommand *GitCommand) ResolveProjectName() string {
	dirs := strings.Split(gitCommand.GitSshUrl, "/")
	dir := dirs[len(dirs)-1]
	dirNotFix := strings.ReplaceAll(dir, ".git", "")
	return dirNotFix
}
func (gitCommand *GitCommand) Init() (output string, err error) {
	if gitCommand.WorkDir == "" {
		gitCommand.WorkDir = "./"
	}
	fmt.Println(helper.GetCurrentRunningPath())
	fmt.Println(gitCommand.WorkDir + gitCommand.ProjectDir)
	if ok, _ := helper.PathExists(gitCommand.WorkDir + gitCommand.ProjectDir); !ok {
		helper.Mkdir(gitCommand.WorkDir+gitCommand.ProjectDir, 0777)
	}
	gitCommand.GitProjectPath = gitCommand.WorkDir + "/" + gitCommand.ProjectDir + gitCommand.ResolvePath()
	if ok, _ := helper.PathExists(gitCommand.GitProjectPath); !ok {
		return gitCommand.clone()
	}
	return "", nil

}
func (gitCommand *GitCommand) Branch() {

}
func (gitCommand *GitCommand) baseCommand(command string, useStd ...bool) (string, error) {
	var res string
	var err error
	if gitCommand.Std != nil && len(useStd) == 0 {
		res, err = GetKernelDriver().ExecShellOrBashWithStdWriter(gitCommand.buildCommand(command), gitCommand.Std)
	} else {
		res, err = GetKernelDriver().ExecShellOrBash(gitCommand.buildCommand(command), false)
	}
	if err != nil {
		GetLogger().Error("git 操作异常:" + err.Error())
	}
	return res, err

}
func (gitCommand *GitCommand) CheckOut(branch string) (string, error) {
	return gitCommand.baseCommand("git checkout -f " + branch)
}

func (gitCommand *GitCommand) ResetHard(commitId string) (string, error) {
	return gitCommand.baseCommand(commonhelper.Format("git reset --hard {}", commitId))
}

func (gitCommand *GitCommand) GetTag() (string, error) {
	tag, err := gitCommand.baseCommand("git rev-parse --short HEAD", false)
	if err == nil {
		tag = strings.Trim(tag, "\n")
	}
	return tag, err
}

func (gitCommand *GitCommand) GetCommitMessage(commitId string) (string, error) {
	tag, err := gitCommand.baseCommand(commonhelper.Format("git show --no-patch --format=%B {}", commitId), false)
	if err == nil {
		tag = strings.Trim(tag, "\n")
	}
	return tag, err
}

func (gitCommand *GitCommand) PullAll(branch string) (string, error) {
	return gitCommand.baseCommand(commonhelper.Format("git reset --hard origin/{} && git pull ", branch))
}
func (gitCommand *GitCommand) SetStd(std *Standard) {
	gitCommand.Std = std
}

func (gitCommand *GitCommand) buildCommand(command string) string {
	fmt.Println("cd " + gitCommand.GitProjectPath + " && " + command)
	return "cd " + gitCommand.GitProjectPath + " && " + command
}

func (gitCommand *GitCommand) CommitLog() ([]GitLog, error) {
	tmpFileName := commonhelper.Md5(strconv.FormatInt(timeHelper.NowInt(), 10)+commonhelper.Rand(20)) + ".log"
	logs, err := gitCommand.baseCommand("git --no-pager log --date=format:%Y-%m-%d@%H:%M:%S >" + tmpFileName)
	if err != nil {
		return nil, err
	}
	logs = helper.ReadFileAsString(gitCommand.GitProjectPath + "/" + tmpFileName)
	helper.RemoveAllFile(gitCommand.GitProjectPath + "/" + tmpFileName)
	logs = "\n" + logs
	logList := strings.Split(logs, "\ncommit ")
	commitLogReg, _ := regexp.Compile("^(.*?)\n")
	regx, err := regexp.Compile("Author:([\\s\\S]*?)Date:([\\s\\S]*?)\n\n([\\s\\S]*?)$")
	if err != nil {
		return nil, err
	}
	res := make([]GitLog, 0)
	for _, item := range logList {
		matchs := regx.FindStringSubmatch(item)
		if len(matchs) == 4 {
			gitlog := GitLog{
				Author:  strings.TrimSpace(matchs[1]),
				Date:    strings.ReplaceAll(strings.TrimSpace(matchs[2]), "@", " "),
				Comment: strings.TrimSpace(matchs[3]),
			}
			commitIdMatch := commitLogReg.FindStringSubmatch(item)
			if len(commitIdMatch) == 2 {
				gitlog.CommitId = strings.TrimSpace(commitIdMatch[1])
				res = append(res, gitlog)
			}
		}

	}
	return res, nil
}

func (item *GitDiffFileItem) handleContent() {
	res := ""
	contentList := strings.Split(item.Content, "\n")
	addLineStart := item.oldStartLine
	reduceLineStart := item.oldStartLine
	//newStartLine:=item.newStartLine
	for _, line := range contentList {
		if len(line) > 0 {
			firstChar := line[0:1]
			switch firstChar {
			case "+":
				addLineStart += 1
				line = strconv.Itoa(addLineStart) + SPLIT_CHAR + line
				break
			case "-":
				reduceLineStart += 1
				line = strconv.Itoa(reduceLineStart) + SPLIT_CHAR + line
				break
			default:
				addLineStart += 1
				reduceLineStart += 1
				line = strconv.Itoa(addLineStart) + SPLIT_CHAR + line
			}
		}
		res += line + "\n"
	}
	item.Content = res
}
